
#ifndef _TTG_MATH_MAT3X3_
#define _TTG_MATH_MAT3X3_
#include "Common/Definitions.h"

#include "vec3.h"

#ifdef NVIDIA_PHYSX
#include "NxMat33.h"
#endif

#include "Matrix.h"

namespace ttg
{
	namespace math
	{
		template <class T> class mat3x3;
		template <class T> class mat4x4;

		template <class T>
		inline mat3x3<T> matrixRotationX(T angle)
		{
			mat3x3<T> res;
			float cosa = cosf((float)angle);
			float sina = sinf((float)angle);
			res.a[0] = 1.0f;
			res.a[4] = cosa;
			res.a[5] = -sina;
			res.a[7] = sina;
			res.a[8] = cosa;
			return res;
		}

		template <class T>
		inline mat3x3<T> matrixRotationY(T angle)
		{
			mat3x3<T> res;
			float cosa = cosf((float)angle);
			float sina = sinf((float)angle);
			res.a[0] = cosa;
			res.a[2] = sina;
			res.a[4] = 1.0f;
			res.a[6] = -sina;
			res.a[8] = cosa;
			return res;
		}

		template <class T>
		inline mat3x3<T> matrixRotationZ(T angle)
		{
			mat3x3<T> res;
			float cosa = cosf((float)angle);
			float sina = sinf((float)angle);
			res.a[0] = cosa;
			res.a[1] = -sina;
			res.a[3] = sina;
			res.a[4] = cosa;
			res.a[8] = 1.0;
			return res;
		}

		template <class T>
		inline mat3x3<T> matrixRotationXYZ(T x, T y, T z)
		{
			return matrixRotationX(x) *
				   matrixRotationY(y) *
				   matrixRotationZ(z);
		}

		//Creates rotation matrix around axis for angle, which cosinus is equal to "cosa".
		//Note: angle will belong [0, Pi].
		template <class T>
		inline mat3x3<T> matrixRotationAxisCos(const vec3<T> &axis, float cosa)
		{
			mat3x3<T> res;
			float c = cosa;
			float s = sqrtf(1 - c * c);
			float ic = 1 - c;
			res.a[0] = axis.x * axis.x * ic + c;
			res.a[1] = axis.x * axis.y * ic - axis.z * s;
			res.a[2] = axis.x * axis.z * ic + axis.y * s;
			res.a[3] = axis.x * axis.y * ic + axis.z * s;
			res.a[4] = axis.y * axis.y * ic + c;
			res.a[5] = axis.y * axis.z * ic - axis.x * s;
			res.a[6] = axis.x * axis.z * ic - axis.y * s;
			res.a[7] = axis.y * axis.z * ic + axis.x * s;
			res.a[8] = axis.z * axis.z * ic + c;
			return res;
		}

		//Creates rotation matrix around axis.
		//Note: angle should belong [0, 2 * Pi].
		template <class T>
		inline mat3x3<T> matrixRotationAxis(const vec3<T> &axis, float angle)
		{
			mat3x3<T> res;
			float s = sinf(angle);
			float c = cosf(angle);
			float ic = 1 - c;
			res.a[0] = axis.x * axis.x * ic + c;
			res.a[1] = axis.x * axis.y * ic - axis.z * s;
			res.a[2] = axis.x * axis.z * ic + axis.y * s;
			res.a[3] = axis.x * axis.y * ic + axis.z * s;
			res.a[4] = axis.y * axis.y * ic + c;
			res.a[5] = axis.y * axis.z * ic - axis.x * s;
			res.a[6] = axis.x * axis.z * ic - axis.y * s;
			res.a[7] = axis.y * axis.z * ic + axis.x * s;
			res.a[8] = axis.z * axis.z * ic + c;
			return res;
		}

		//Creates matrix, which rotates v1 into v2.
		template <class T>
		inline mat3x3<T> matrixRotationVectors(const vec3<T> &v1, const vec3<T> &v2)
		{
			vec3<T> nv1 = v1.normalize();
			vec3<T> nv2 = v2.normalize();
			float cosa = dotProduct(nv1, nv2);
			vec3<T> axis = crossProduct(nv1, nv2).normalize();
			return matrixRotationAxisCos(axis, cosa);;
		}

		#ifdef MICROSOFT_DIRECTX

		//Creates DX matrix for given transformation.
		template <class T>
		inline D3DXMATRIX matrixTransform(const mat3x3<T> &rot, const vec3<T> &pos, T scale)
		{
			D3DXMATRIX pos_scale = D3DXMATRIX(scale, 0.0f, 0.0f, 0.0f,
											  0.0f, scale, 0.0f, 0.0f,
											  0.0f, 0.0f, scale, 0.0f,
											  pos.x, pos.y, pos.z, 1.0f);
			return (D3DXMATRIX)rot * pos_scale;
		}

		#endif

		template <class T>
		inline mat3x3<T> matrixTranspose(const mat3x3<T> &mat) 
		{
			mat3x3<T> res;
			res.a[0] = mat.a[0];
			res.a[1] = mat.a[3];
			res.a[2] = mat.a[6];
			res.a[3] = mat.a[1];
			res.a[4] = mat.a[4];
			res.a[5] = mat.a[7];
			res.a[6] = mat.a[2];
			res.a[7] = mat.a[5];
			res.a[8] = mat.a[8];
			return res;
		}

		template <class T>
		inline mat3x3<T> matrixIdentity()
		{
			mat3x3<T> res;
			res.a[0] = 1.0f;
			res.a[4] = 1.0f;
			res.a[8] = 1.0f;
			return res;
		}

		template <class T>
		class mat3x3 :public Matrix
		{
			public:
				T a[9]; //row-based implementation: row1-row2-row3

				//Constructors
			    
				inline mat3x3()
				{
					for (int i=0; i<9; i++)
						a[i] = 0.0f;
				}

				inline mat3x3(const mat3x3<T> &mat)
				{
					for (int i = 0; i < 9; i++)
						a[i] = mat.a[i];
				}

		#ifdef MICROSOFT_DIRECTX

				inline mat3x3(const D3DXMATRIX &mat)
				{
					a[0] = mat._11;
					a[1] = mat._21;
					a[2] = mat._31;
					a[3] = mat._12;
					a[4] = mat._22;
					a[5] = mat._32;
					a[6] = mat._13;
					a[7] = mat._23;
					a[8] = mat._33;
				}

		#endif

		#ifdef NVIDIA_PHYSX

				inline mat3x3(const NxMat33 &mat)
				{
					a[0] = mat(0, 0);
					a[1] = mat(0, 1);
					a[2] = mat(0, 2);
					a[3] = mat(1, 0);
					a[4] = mat(1, 1);
					a[5] = mat(1, 2);
					a[6] = mat(2, 0);
					a[7] = mat(2, 1);
					a[8] = mat(2, 2);
				}

		#endif

				inline mat3x3(T _11, T _12, T _13,
							  T _21, T _22, T _23,
							  T _31, T _32, T _33)
				{
					a[0] = _11;
					a[1] = _12;
					a[2] = _13;
					a[3] = _21;
					a[4] = _22;
					a[5] = _23;
					a[6] = _31;
					a[7] = _32;
					a[8] = _33;
				}

				//Operations

				inline mat3x3<T> operator +(const mat3x3<T> &mat) const
				{
					mat3x3<T> res;
					for (int i = 0; i < 9; i++)
						res.a[i] = this->a[i] + mat.a[i];
					return res;
				}

				inline mat3x3<T> operator -(const mat3x3<T> &mat) const
				{
					mat3x3<T> res;
					for (int i = 0; i < 9; i++)
						res.a[i] = this->a[i] - mat.a[i];
					return res;
				}

				inline mat3x3<T> operator *(T val) const
				{
					mat3x3<T> res;
					for (int i = 0; i < 9; i++)
						res.a[i] = this->a[i] * val;
					return res;
				}
			    
				inline vec3<T> operator *(const vec3<T> &vec) const
				{
					return vec3<T>(a[0]*vec.x + a[1]*vec.y + a[2]*vec.z,
								   a[3]*vec.x + a[4]*vec.y + a[5]*vec.z,
								   a[6]*vec.x + a[7]*vec.y + a[8]*vec.z);
				};
			    
				inline mat3x3<T> operator *(const mat3x3<T> &mat) const
				{
					mat3x3<T> res;
					res.a[0] = this->a[0]*mat.a[0] +
							   this->a[1]*mat.a[3] +
							   this->a[2]*mat.a[6];
					res.a[1] = this->a[0]*mat.a[1] +
							   this->a[1]*mat.a[4] +
							   this->a[2]*mat.a[7];
					res.a[2] = this->a[0]*mat.a[2] +
							   this->a[1]*mat.a[5] +
							   this->a[2]*mat.a[8];
					res.a[3] = this->a[3]*mat.a[0] +
							   this->a[4]*mat.a[3] +
							   this->a[5]*mat.a[6];
					res.a[4] = this->a[3]*mat.a[1] +
							   this->a[4]*mat.a[4] +
							   this->a[5]*mat.a[7];
					res.a[5] = this->a[3]*mat.a[2] +
							   this->a[4]*mat.a[5] +
							   this->a[5]*mat.a[8];
					res.a[6] = this->a[6]*mat.a[0] +
							   this->a[7]*mat.a[3] +
							   this->a[8]*mat.a[6];
					res.a[7] = this->a[6]*mat.a[1] +
							   this->a[7]*mat.a[4] +
							   this->a[8]*mat.a[7];
					res.a[8] = this->a[6]*mat.a[2] +
							   this->a[7]*mat.a[5] +
							   this->a[8]*mat.a[8];
					return res;
				}

				inline mat3x3<T> operator /(T val) const
				{
					return (*this) * (1.0f / val);
				}

				inline mat3x3<T> operator /(const mat3x3<T> &mat) const
				{
					return (*this) * matrixTranspose(mat);
				}
			    
				inline const mat3x3<T> &operator =(const mat3x3<T> &mat)
				{
					for (int i=0; i<9; i++)
						this->a[i] = mat.a[i];
					return *this;
				}

				inline bool operator ==(const mat3x3<T> &mat) const
				{
					float v = 0;
					for (int i = 0; i < 9; i++)
						v += fabs(a[i] - mat.a[i]);
					return isZero(v);
				}

				inline bool operator !=(const mat3x3<T> &mat) const
				{
					return !((*this) == mat);
				}

		#ifdef MICROSOFT_DIRECTX

				inline operator D3DXMATRIX() const
				{
					return D3DXMATRIX(this->a[0], this->a[3], this->a[6], 0,
									  this->a[1], this->a[4], this->a[7], 0,
									  this->a[2], this->a[5], this->a[8], 0,
									  0,		  0,		  0,		  1);
				}

		#endif

		#ifdef NVIDIA_PHYSX

				inline operator NxMat33() const
				{
					NxMat33 mat;
					mat(0, 0) = this->a[0];
					mat(0, 1) = this->a[1];
					mat(0, 2) = this->a[2];
					mat(1, 0) = this->a[3];
					mat(1, 1) = this->a[4];
					mat(1, 2) = this->a[5];
					mat(2, 0) = this->a[6];
					mat(2, 1) = this->a[7];
					mat(2, 2) = this->a[8];
					return mat;
				}

		#endif

				virtual int getRank()
				{
					return 3;
				}

				virtual int getComponentSize()
				{
					return sizeof(T);
				}
		};
	}
}

#endif
